use anyhow::Result;
use serialport::{self, SerialPort};
use std::time::Duration;

const BAUDRATE: u32 = 115_200;
const COMMAND_READ: &[u8] = &[0x01, 0x00, 0x00, 0xF0, 0x10, 0x00, 0x00, 0x00];
const _COMMAND_NONE: &[u8] = &[];

#[derive(Clone, Debug, Default)]
pub struct DDAResult {
    pub ddatype: String,
    pub content: Vec<u8>,
}

pub struct ProDDA {
    pub port: Box<dyn SerialPort>,
}

impl ProDDA {
    pub fn open(port_name: String) -> Result<Box<dyn SerialPort>> {
        let wait_time = Duration::from_millis(1);
        let port_build = serialport::new(port_name, BAUDRATE);
        let port: Box<dyn SerialPort> = port_build.timeout(wait_time).open()?;
        Ok(port)
    }

    pub fn init(port_name: String) -> Result<Self> {
        let result = ProDDA {
            port: ProDDA::open(port_name)?,
        };
        Ok(result)
    }

    pub async fn port_read(&mut self, mut base: Vec<u8>, len: usize) -> Result<Vec<u8>> {
        if self.port.bytes_to_read()? > 0 {
            let mut read_buf: Vec<u8> = vec![0; len];
            let bytes_read = self.port.read(read_buf.as_mut_slice())?;
            read_buf.resize(bytes_read, 0);
            base.extend(read_buf);
            // println!("DDA Read:{:02X?}", read_buf);
        }
        Ok(base)
    }

    async fn port_pack_read(&mut self) -> Result<Vec<u8>> {
        let mut result = self.port_read(Vec::new(), 8).await?;
        if result.len() < 8 && result.len() > 0 {
            if !result.iter().all(|&x| x == 0) {
                tokio::time::sleep(Duration::from_millis(5)).await;
                let remain = 8 - result.len();
                result = self.port_read(result, remain).await?;
                println!("DDA CMD AutoPack:{:02X?}", result);
            } else {
                println!("DDA CMD All 0x00:{:02X?}", result);
            }
        }

        Ok(result)
    }

    // 刚好在这里处理串口掉线的问题
    pub async fn parse_await(&mut self) -> Result<DDAResult> {
        loop {
            let data = self.port_pack_read().await?;
            if !data.is_empty() {
                let result_type = match data.as_slice() {
                    COMMAND_READ => "Read",
                    _ => "Other",
                };
                let mut result = DDAResult::default();
                result.ddatype = result_type.to_string();
                result.content = data;
                return Ok(result);
            };
            tokio::time::sleep(Duration::from_millis(5)).await;
        }
    }
}
